﻿using System;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Caching;
using Medianamik.Core;
using Medianamik.Core.Bootstrapping;
using Medianamik.Core.CodeGeneration;
using Medianamik.Core.Configuration;
using Medianamik.Core.Redirect;
using Medianamik.Core.Sugar;
using Medianamik.Core.Urls;
using Medianamik.DummyWebSite.Container;
using Medianamik.UI.Web.Services;
using Utility = Medianamik.Client.Utility;

namespace Medianamik.DummyWebSite
{
    public class Global : HttpApplication
    {
        private static readonly MedianamikConfigSection Config =
            (MedianamikConfigSection)ConfigurationManager
                .GetSection(ConfigurationSectionName.MEDIANAMIK);
        private static readonly CacheItemRemovedCallback OnCacheRemove = CacheItemRemoved;
        private static readonly ResourceService ResourceService = new ResourceService();

        protected void Application_Start(object sender, EventArgs e)
        {
            new Utility().GetGlobalLogger().Trace("Application_Start");
            SetUpCacheDependency();
            ContainerBootstrapper.BootstrapStructureMap();
            AddScheduledTasks();
            ResourceService.MainAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
        }

        private static void AddScheduledTasks()
        {
            if (Config.EnableAutoResourceUpdate)
            {
                AddTask(MedianamikTasks.UpdateResources, Config.ResourceUpdateInterval);
            }

            if (Config.EnableCacheRecycler)
            {
                AddTask(MedianamikTasks.AddCacheRecycler, 30);
            }

            AddTask(MedianamikTasks.CleanUpNodeRevisions, 1200);
        }

        private static void SetUpCacheDependency()
        {
            try
            {
                if (!SqlCacheDependencyAdmin
                         .GetTablesEnabledForNotifications(ConfigurationManager
                            .ConnectionStrings["cs_dbo"].ConnectionString)
                                .Any(t => t.Equals("medianamik_urlcache",
                                    StringComparison.OrdinalIgnoreCase)))
                {
                    SqlCacheDependencyAdmin.EnableTableForNotifications(ConfigurationManager.
                        ConnectionStrings["cs_dbo"].ConnectionString, "medianamik_urlcache");
                }
            }
            catch (Exception)
            {
                new Utility().GetGlobalLogger().Fatal("Veuillez exécuter les mises à jour SQL.");
            }
        }

        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            Bootstrapper.GetBootstrapper().SafeConfigure(this);
        }

        protected void Application_End(object sender, EventArgs e)
        {
            Bootstrapper.GetBootstrapper().Cleanup(this);

            var runtime = (HttpRuntime)typeof(HttpRuntime).InvokeMember("_theRuntime",
                BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField,
                null, null, null);

            if (runtime == null)
                return;

            var shutDownMessage = (string)runtime.GetType().InvokeMember("_shutDownMessage",
                BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
                null, runtime, null);

            var shutDownStack = (string)runtime.GetType().InvokeMember("_shutDownStack",
                BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
                null, runtime, null);

            new Utility().GetGlobalLogger()
                .Trace(String.Format("\r\n\r\n_shutDownMessage={0}\r\n\r\n_shutDownStack={1}",
                    shutDownMessage, shutDownStack));
        }

        private static void AddTask(string name, int seconds)
        {
            HttpRuntime.Cache.Insert(name, seconds, null,
                DateTime.Now.AddSeconds(seconds), Cache.NoSlidingExpiration,
                CacheItemPriority.NotRemovable, OnCacheRemove);
        }

        private static void CacheItemRemoved(string taskName, object seconds, CacheItemRemovedReason r)
        {
            switch (taskName)
            {
                case MedianamikTasks.UpdateResources:
                    if (Config.EnableAutoResourceUpdate)
                    {
                        ResourceService.UpdateResourcesFromService();
                        AddTask(taskName, Convert.ToInt32(seconds));
                    }
                    break;
                case MedianamikTasks.AddCacheRecycler:
                    if (Config.EnableCacheRecycler)
                    {
                        RecycleCache();
                        AddTask(MedianamikTasks.AddCacheRecycler, 30);
                    }
                    break;
                case MedianamikTasks.CleanUpNodeRevisions:
                    CleanNodeRevisions();
                    AddTask(MedianamikTasks.CleanUpNodeRevisions, 1200);
                    break;
            }
        }

        private static void RecycleCache()
        {
            new Utility().GetGlobalLogger().Trace("Recycling Cache");

            try
            {
                new ObjectFactory<IUrlDictionary>().Get().Reload();
                ExternalRedirectDictionary.Instance.Reload();
                RedirectDictionary.Instance.Reload();
                new Utility().GetGlobalLogger().Trace("Cache Recycled");
            }
            catch (Exception ex)
            {

                new Utility().GetGlobalLogger().Trace("Error While Recycling Cache: " + ex.ToStringWithDetails());
            }
        }

        private static void CleanNodeRevisions()
        {
            new Utility().GetGlobalLogger().Trace("Cleaning Node Revisions");

            try
            {
                var codeService = new CodeService();
                codeService.CleanUpNodeRevisions();
                new Utility().GetGlobalLogger().Trace("Node Revisions Cleaned");
            }
            catch (Exception ex)
            {
                new Utility().GetGlobalLogger().Trace("Error While Cleaning Node Revisions: " + ex.ToStringWithDetails());
            }
        }
    }
}
